#
#  This library which provides the functions to access the data stored in a
#  table file (a csv file with the space or tab as delimiter) in a script.
#  Each row is a data set, and the variables are named like defined in the
#  header line of the table file plus a prefix.
#
#  Synopsis:
#		protbl_getkeys FILE
#		protbl_dims FILE
#		protbl_check FILE [v(erbose)]
#		protbl_func FILE FUNC [VPREFIX]
#		protbl_func_where FILE FUNC KEY VALUE [VPREFIX]
#		protbl_row FILE #ROW [VPREFIX]
#

# =============================================================================
# DEFAULT LIBRARY VARIABLES
# =============================================================================

# Default prefix for table variables:
VPREFIX='tbl'

# Should protbl exit (1) or cast only a warning and skip (0) if number
# of columns in a row is inconsistent with number of keys in header line?
HCHECK=0

# =============================================================================
# INTERNAL FUNCTIONS
# =============================================================================

# Returns a 'cleaned' list.
___cll (){
	echo $1 | sed -e 's/[[:space:]]\+/ /g'
}

# Define a variable.
# $1: variable name (key)
# $2: value
___def (){
	eval "$1='$2'"
}

# Unset a variable.
# $1: variable name (key)
___uset (){
	eval "unset $1"
}

# Get an element/word from a list of strings.
# $1: list
# $2: position
___sel (){
	echo "$1" | cut -d' ' -f$2
}

# Check if number of words/entries in two list 
# ($1, $2) are equal (1) or not (0).
___cequal (){
	if [ $(echo $1 | wc -w) -eq $(echo $2 | wc -w) ]; then
		echo 1
	else
		echo 0
	fi
}

# Define variables given by a list of
# variable keys and values.
# $1: list of keys
# $2: list of values
# $3: prefix (optional)
___deflist (){
	local k v i
	for i in $( seq 1 $(echo $1| wc -w) ); do
		k=$(___sel "$1" $i)
		v=$(___sel "$2" $i)
		___def "$3$k" $v
	done
}

# Unset variables given by a list of
# variable keys and values.
# $1: list of keys
# $2: prefix (optional)
___unsetlist (){
	local k i
	for i in $( seq 1 $(echo $1| wc -w) ); do
		k=$(___sel "$1" $i)
		___def "$2$k" $v
	done
}

# Check if file ($1) exists.
___filexit (){
	if [ ! -f $1 ]; then
		echo "(protbl.sh) ERROR: File $1 did not exist!" >&2
		exit 1
	fi
}

# Add a prefix ($2) to all elements of list of strings ($1).
___addpf2list (){
	local element nlist
	nlist=''
	for element in $1 ; do
		nlist="$nlist ${2}${element}"
		echo $element
	done
	echo $nlist
}

# Extract the keys from header line of table file ($1).
___getkeys (){	
	local out
	___filexit $1
	out=$(cat $1 | grep -e '^#' | cut -d' ' -f2-)
	out=$(___cll "$out")
	if [ $(echo $out | wc -w) -eq 0 ]; then
		echo "(protbl.sh) ERROR: Can\'t find header line!" >&2
		exit 1
	else
		echo $out
	fi
}

# Get the variable prefix
# $1: prefix (optional)
___getprefix (){
	local pf
	if [ "$1" = '' ]; then
		pf="${VPREFIX}_"
	else
		pf="$1_"
	fi
	echo $pf
}

___ncols (){
	___getkeys $1 | wc -w
}

___nrows (){
	cat $1  | sed '/^#/ d'| wc -l
}

___getline (){
	local line
	if [ $2 -le $(___nrows $1) -a $2 -ge 1 ]; then
		line=$(cat $1 | sed -e '/^#/ d' | head -n $2 | tail -n 1)
		echo $(___cll "$line")
	else
		echo "(protbl.sh) ERROR: Requested data rows #$2 didn\'t exists in '\$1\'!" >&2
		exit 1
	fi
}

# Set variables
# $1: FILE    $2: KEYS
# $3: VALUES  $4: PREFIX
# $5: ROW
___setvariables() {
	if [ $(___cequal "$2" "$3") -eq 0 ]; then
		if [ $HCHECK -eq 1 ]; then
			echo "(protbl.sh) ERROR: Row #$5 in file $1 has an inconsistent number of columns!" >&2
			exit 1
		else
			echo "(protbl.sh) WARNING: Row #$5 in file $1 has an inconsistent number of columns, I skip it!" >&2
		fi
		___unsetlist "$2" "$4"
		is_set=1
	else
		___deflist "$2" "$3" "$4"
		is_set=0
	fi
}

# Returns the column numer of a KEY.
# $1: FILE    $2: KEY
___key2pos() {
	local pos i
	pos=0; i=0
	for key in $(___getkeys $1); do
		i=$(($i+1))
		[ "$key" = "$2" ] && pos=$i
	done
	if [ $pos -eq 0 ]; then
		echo "(protbl.sh) ERROR: Key \'$2\' not found in file \'$1\'!" >&2
		exit 1
	else
		echo $pos
	fi
}

# =============================================================================
# USER FUNCTIONS
# =============================================================================

# Extract the keys from header of table file ($1).
getkeys (){
	local keys
	keys=$( ___getkeys $1 )
	[ ! "$keys" ] && exit 1
	echo $keys
}

# Returns the dimensions of the table -> #columns,#rows
# $1: table file
dims (){
	local nc nr
	nc=$( ___ncols $1 )
	[ $nc -eq 0 ] && exit 1
	nr=$( ___nrows $1 )
	echo "$nc,$nr"
}

# Check consistents of table
# $1: table file
# $2: verbose? (optional)
check (){
	local keys values line
	keys=$( ___getkeys $1 )
	[ ! "$keys" ] && exit 1
	no=0; ne=0; el=''
	while read line; do
		if [ $(echo $line | cut -d' ' -f1) != "#" ]; then
			no=$(($no+1))
			values=$( ___cll "$line" )
			if [ $(___cequal "$keys" "$values") -eq 0 ]; then
				ne=$(($ne+1))
				el="$el $no"
			fi
		fi
	done < $1
	if [ "$2" = '' ]; then
		echo $ne
	else
		if [ $ne -gt 0 ]; then		
			echo -n "ERROR! Row "$(echo $el| sed 's/ /,/g')" in table file \'$1\' "
			echo "contain(s) less/more then "$(___getkeys $1 | wc -w)" columns."
		else
			echo -n "OK! All $no rows in table file \'$1\' "
			echo "are consistent and contain "$(___getkeys $1 | wc -w)" columns."
		fi
	fi
}

# Call a function for each data row in table file and
# providing the respective values for each call.
# $1: table file
# $2: function
# $3: prefix for table variables
func (){
	local keys values no pf
	keys=$( ___getkeys $1 )
	[ ! "$keys" ] && exit 1
	pf=$( ___getprefix $3 )
	no=0
	cat $1  | sed '/^#/ d'| while read line; do
		no=$(($no+1))
		values=$( ___cll "$line" )
		___setvariables $1 "$keys" "$values" "$pf" $no
		[ $is_set -eq 0 ] && $2
	done
}

# Call a function for each data row in table file 
# where KEY = VALUE and providing the respective values for each call.
# $1: table file
# $2: function
# $3: key
# $4: value
# $5: prefix for table variables
func_where (){
	local keys values no pf pos kvalue
	keys=$( ___getkeys $1 )
	[ ! "$keys" ] && exit 1
	pf=$( ___getprefix $5 )
	pos=$( ___key2pos $1 $3 )
	[ $? -eq 1 ] && exit 1
	no=0
	cat $1  | sed '/^#/ d'| while read line; do
		no=$(($no+1))
		values=$( ___cll "$line" )
		kvalue=$(echo "$values" | cut -d' ' -f$pos)
		if [ "$kvalue" = "$4" ]; then
			___setvariables $1 "$keys" "$values" "$pf" $no
			[ $is_set -eq 0 ] && $2
		fi
	done
}

# Returns the data of a given row.
# $1: table file
# $2: #row
# $3: prefix for table variables
row (){
	keys=$( ___getkeys $1 )
	[ ! "$keys" ] && exit 1
	pf=$( ___getprefix $3 )
	values=$( ___getline $1 $2 )
	[ $? -eq 1 ] && exit 1
	___setvariables $1 "$keys" "$values" "$pf" $2
}



